package main

import (
	"fmt"
	"strconv"
)

const fileFilter = "PRS bin File(*.bin);;All File(*)"

type Root = ImagePrsFile

type ImagePrsFile struct {
	Header       PrsHeader
	Strings      []StringItem
	Function     []FunctionItem
	FunctionData [][]PrsFunctionData
}

func (v *ImagePrsFile) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.SetArg(v)
	dec.Decode(&v.Header, &ValueInfo{name: "Header"})
	v.Strings = make([]StringItem, v.Header.StringListCount)
	dec.Decode(&v.Strings, &ValueInfo{name: "Strings"})
	v.Function = make([]FunctionItem, v.Header.FunctionListCount)
	dec.Decode(&v.Function, &ValueInfo{name: "Function"})
	var buf []byte
	dec.Decode(&buf, &ValueInfo{name: "FunctionData"})

	pos := dec.Pos()
	defer dec.Seek(pos)

	v.FunctionData = make([][]PrsFunctionData, len(v.Function))
	for i, f := range v.Function {
		dec.Seek(int(f.Offset) * 8)
		newDec := dec.SubDecoder(int(f.Length))
		newDec.Decode(&v.FunctionData[i], &ValueInfo{name: string(f.Name), info: f.Type.String()})
	}
}

type PrsHeader struct {
	MajorVersion       uint16
	MinorVersion       uint16
	StringListOffset   uint16
	StringListCount    uint16
	FunctionListOffset uint16
	FunctionListCount  uint16
}

type StringItem struct {
	pos uint16
	Len uint16
	Str string
}

func (v *StringItem) Unmarshal(dec *Decoder, info *ValueInfo) {
	v.pos = uint16(dec.Pos() / 8)
	dec.Decode(&v.Len, &ValueInfo{name: "StringLength"})
	newDec := dec.SubDecoder(int(v.Len))
	var bs []byte
	newDec.Decode(&bs, &ValueInfo{name: "String"})
	dec.Seek(newDec.Pos())
	v.Str = string(bs)
}

func (v StringItem) String() string {
	return v.Str
}

type FunctionItem struct {
	Name   PrsString
	Length uint16
	Offset uint32
	Type   FunctionType
}

func (v *FunctionItem) String() string {
	return fmt.Sprintf("func %s return %s", v.Name, v.Type.String())
}

type FunctionType uint32

func (v FunctionType) String() string {
	switch v {
	case 0x01:
		return "item"
	case 0x02:
		return "addr"
	case 0x03:
		return "default"
	default:
		return strconv.Itoa(int(v))
	}
}

type PrsString string

func (v *PrsString) Unmarshal(dec *Decoder, info *ValueInfo) {
	prs, ok := dec.Arg().(*ImagePrsFile)
	if !ok || prs == nil {
		panic("decode arg not set")
	}

	var pos uint16
	dec.Decode(&pos, info)
	pos += prs.Header.StringListOffset

	for _, str := range prs.Strings {
		if str.pos == pos {
			*v = PrsString(str.Str)
			return
		}
	}
	panic("unknown string at " + strconv.Itoa(int(pos)))
}

type PrsFunctionData struct {
	Arg   uint8
	Cmd   FunctionCmd
	Name  PrsString
	Key   PrsString
	Value PrsString
	Index uint8
}

func (v *PrsFunctionData) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.Decode(&v.Arg, &ValueInfo{name: "Arg", bit: 3})
	dec.Decode(&v.Cmd, &ValueInfo{name: "Cmd", bit: 5})
	if v.Cmd == 6 || v.Cmd == 10 {
		return
	}
	dec.Decode(&v.Name, &ValueInfo{name: "Name"})
	switch v.Arg {
	case 0:
		break
	case 1:
		dec.Decode(&v.Index, &ValueInfo{name: "Index"})
	case 2:
		dec.Decode(&v.Key, &ValueInfo{name: "Key"})
		dec.Decode(&v.Value, &ValueInfo{name: "Value"})
	case 3:
		dec.Decode(&v.Key, &ValueInfo{name: "Key"})
		dec.Decode(&v.Value, &ValueInfo{name: "Value"})
		dec.Decode(&v.Index, &ValueInfo{name: "Index"})
	default:
		break
	}
}

func (v *PrsFunctionData) String() string {
	if v.Cmd == 6 || v.Cmd == 10 {
		return v.Cmd.String()
	}
	switch v.Arg {
	case 1:
		return fmt.Sprintf("%s %s %d", v.Cmd.String(), v.Name, v.Index)
	case 2:
		return fmt.Sprintf("%s %s %s \"%s\"", v.Cmd.String(), v.Name, v.Key, v.Value)
	case 3:
		return fmt.Sprintf("%s %s %s \"%s\" %d", v.Cmd.String(), v.Name, v.Key, v.Value, v.Index)
	default:
		return fmt.Sprintf("%s %s", v.Cmd.String(), v.Name)
	}
}

type FunctionCmd uint8

func (v FunctionCmd) String() string {
	textMap := []string{
		2:  "in",
		4:  "goto",
		5:  "gotol",
		6:  "return",
		8:  "attr",
		10: "value",
		12: "cut",
		13: "cutl",
		14: "pass",
		15: "passl",
		16: "id",
	}
	if int(v) < len(textMap) && len(textMap[v]) > 0 {
		return textMap[v]
	}
	return strconv.Itoa(int(v))
}
